package application.net;

import application.anno.Injection;
import application.anno.Sockets;
import application.exception.NotFoundNecessaryAnnotationException;
import application.manager.InjectionManager;
import application.util.ReflectionsUtil;
import okhttp3.*;
import okio.ByteString;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Injection(resource = "192.168.7.12:8080/webSocket/1576")
public class ClientSocket extends WebSocketListener {

    public static final int ON_OPEN_METHOD = 0x01;
    public static final int ON_MESSAGE_STRING_METHOD = 0x02;
    public static final int ON_MESSAGE_BYTE_METHOD = 0x03;
    public static final int ON_FAILURE_METHOD = 0x04;
    public static final int ON_CLOSING_METHOD = 0x05;
    public static final int ON_CLOSED_METHOD = 0x06;

    private String linkAddress;

    private Map<Integer, Method> methods;

    private Class<?> listener;

    public WebSocket init() throws NotFoundNecessaryAnnotationException {
        Set<Class<?>> annotationByClass = ReflectionsUtil.getAnnotationByClass(Sockets.class);

        for (Class<?> cls : annotationByClass) {
            Sockets sockets = cls.getAnnotation(Sockets.class);
            if (sockets != null && sockets.socketName().equals(getClass().getName())) {
                listener = cls;
            }
        }
        if (listener != null) {
            // 注册各种监听事件
            Method[] declaredMethods = listener.getDeclaredMethods();
            for (Method method : declaredMethods) {
                Sockets temp = method.getAnnotation(Sockets.class);
                if (temp == null) {
                    continue;
                }
                switch (temp.methodFlag()) {
                    case ON_OPEN_METHOD:
                        if (methods == null) {
                            methods = new HashMap<>();
                        }
                        methods.put(ON_OPEN_METHOD, method);
                        break;
                    case ON_MESSAGE_STRING_METHOD:
                        if (methods == null) {
                            methods = new HashMap<>();
                        }
                        methods.put(ON_MESSAGE_STRING_METHOD, method);
                        break;
                    case ON_MESSAGE_BYTE_METHOD:
                        if (methods == null) {
                            methods = new HashMap<>();
                        }
                        methods.put(ON_MESSAGE_BYTE_METHOD, method);
                        break;
                    case ON_FAILURE_METHOD:
                        if (methods == null) {
                            methods = new HashMap<>();
                        }
                        methods.put(ON_FAILURE_METHOD, method);
                        break;
                    case ON_CLOSING_METHOD:
                        if (methods == null) {
                            methods = new HashMap<>();
                        }
                        methods.put(ON_CLOSING_METHOD, method);
                        break;
                    case ON_CLOSED_METHOD:
                        if (methods == null) {
                            methods = new HashMap<>();
                        }
                        methods.put(ON_CLOSED_METHOD, method);
                        break;
                }
            }
        }
        Injection injection = this.getClass().getAnnotation(Injection.class);
        if (injection == null || (injection.resource().isEmpty() && injection.ip().isEmpty())) {
            throw new NotFoundNecessaryAnnotationException("Not found Injection annotation or annotation not have resource, can not get webSocket link resource");
        }
        if (!injection.resource().isEmpty()) {
            linkAddress = "ws://".concat(injection.resource());
        } else if (!injection.ip().isEmpty() && injection.port() > 0) {
            linkAddress = "ws://".concat(injection.ip()).concat(":").concat(String.valueOf(injection.port()));
        }
        OkHttpClient client = new OkHttpClient.Builder().retryOnConnectionFailure(true).build();
        Request request = new Request.Builder().url(linkAddress).build();
        client.dispatcher().cancelAll();//清理一次
        InjectionManager.getInstance().register(this.getClass(), this);
        return client.newWebSocket(request, this);
    }

    @Override
    public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
        super.onClosed(webSocket, code, reason);
        if (methods == null){
            return;
        }
        Method method = methods.get(ON_CLOSED_METHOD);
        if (method != null) {
            Object lookup = InjectionManager.getInstance().lookup(listener);
            if (lookup != null) {
                method.setAccessible(true);
                try {
                    method.invoke(lookup);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Override
    public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
        super.onClosing(webSocket, code, reason);
        if (methods == null){
            return;
        }
        Method method = methods.get(ON_CLOSING_METHOD);
        if (method != null) {
            Object lookup = InjectionManager.getInstance().lookup(listener);
            if (lookup != null) {
                method.setAccessible(true);
                try {
                    method.invoke(lookup);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) {
        super.onFailure(webSocket, t, response);
        if (methods == null){
            return;
        }
        Method method = methods.get(ON_FAILURE_METHOD);
        if (method != null) {
            Object lookup = InjectionManager.getInstance().lookup(listener);
            if (lookup != null) {
                method.setAccessible(true);
                try {
                    method.invoke(lookup);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
        super.onMessage(webSocket, text);
        if (methods == null){
            return;
        }
        Method method = methods.get(ON_MESSAGE_STRING_METHOD);
        if (method != null) {
            Object lookup = InjectionManager.getInstance().lookup(listener);
            if (lookup != null) {
                method.setAccessible(true);
                try {
                    method.invoke(lookup);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) {
        super.onMessage(webSocket, bytes);
        if (methods == null){
            return;
        }
        Method method = methods.get(ON_MESSAGE_BYTE_METHOD);
        if (method != null) {
            Object lookup = InjectionManager.getInstance().lookup(listener);
            if (lookup != null) {
                method.setAccessible(true);
                try {
                    method.invoke(lookup);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
        super.onOpen(webSocket, response);
        if (methods == null){
            return;
        }
        Method method = methods.get(ON_OPEN_METHOD);
        if (method != null) {
            Object lookup = InjectionManager.getInstance().lookup(listener);
            if (lookup != null) {
                method.setAccessible(true);
                try {
                    method.invoke(lookup);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
